std::list for Scheduler

Re-factor the scheduler to use std::list

Because
 - ImplSchedulerData
    - remove: mbInScheduler, mnUpdateTime, mnUpdateStack
	   that is scheduler stuff
	- this struct is only a container for the scheduler-list
 - UpdateMinPeriod
    - the scheduler is the pure-virtual-class then
       the idle-class must override this method
 - ImplDeInitScheduler(bool All=true)
    - this patch 2e29a518b04250b5f9cc9d0d77da3df076834d60 remove
	  all scheduler tasks and the scheduler, but after that,
	  the scheduler is using, then crash.
      With this fix, only delete the scheduler-list, but not the
      scheduler

The next steps
 - split the scheduler from the scheduler-list-handling
   the scheduler-list-handling need a static class
 - remove the scheduler from the timer-handling staff

Change-Id: I8d4d4f27b2bc9684a48c2afafd0b3edd0716c71d
Reviewed-on: https://gerrit.libreoffice.org/16148
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
Tested-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
diff --git a/include/vcl/idle.hxx b/include/vcl/idle.hxx
index 2e853b7..3d235a5 100644
--- a/include/vcl/idle.hxx
+++ b/include/vcl/idle.hxx
@@ -28,6 +28,7 @@
protected:
    Link<Idle *, void> maIdleHdl;          // Callback Link


public:
    Idle( const sal_Char *pDebugName = NULL );
    Idle( const Idle& rIdle );
diff --git a/include/vcl/scheduler.hxx b/include/vcl/scheduler.hxx
index 6c4e211..6b27e05 100644
--- a/include/vcl/scheduler.hxx
+++ b/include/vcl/scheduler.hxx
@@ -21,23 +21,18 @@
#define INCLUDED_VCL_SCHEDULER_HXX

#include <vcl/dllapi.h>
#include <list>

struct ImplSVData;

class Scheduler;
struct ImplSchedulerData
{
    ImplSchedulerData*  mpNext;      // Pointer to the next element in list
    Scheduler*          mpScheduler;      // Pointer to VCL Scheduler instance
    bool                mbDelete;    // Destroy this scheduler?
    bool                mbInScheduler;    // Scheduler currently processed?
    sal_uInt64          mnUpdateTime;   // Last Update Time
    sal_uInt32          mnUpdateStack;  // Update Stack

    void Invoke();

    static ImplSchedulerData *GetMostImportantTask( bool bTimer );
    bool        mbDelete;           // Destroy this scheduler?
    Scheduler*  mpScheduler;        // Pointer to VCL Scheduler instance
};

#define MAX_TIMER_PERIOD    SAL_MAX_UINT64

enum class SchedulerPriority {
    HIGHEST   = 0,
    HIGH      = 1,
@@ -56,6 +51,7 @@
    const sal_Char     *mpDebugName;        /// Useful for debugging
    SchedulerPriority   mePriority;         /// Scheduler priority
    bool                mbActive;           /// Currently in the scheduler
    sal_uInt64          mnUpdateTime;       /// Last Update Time

    friend struct ImplSchedulerData;
    virtual void SetDeletionFlags();
@@ -82,15 +78,28 @@
    bool            IsActive() const { return mbActive; }
    void            SetInActive() { mbActive = false; }

    Scheduler&          operator=( const Scheduler& rScheduler );
    static void ImplDeInitScheduler();
    Scheduler&      operator=( const Scheduler& rScheduler );
    static void ImplDeInitScheduler(bool bAll=true);
    static void ImplInitScheduler();

    // Process one pending Timer with highhest priority
    // Process one pending Timer with highest priority
    static void CallbackTaskScheduling( bool ignore );
    /// Process one pending task ahead of time with highhest priority.
    /// Process one pending task ahead of time with highest priority.
    static void ProcessTaskScheduling( bool bTimer );

private:
    bool                mbInScheduler;   // Scheduler currently processed?
    sal_uInt32          mnUpdateStack;   // Update Stack

    bool  ImplHandleTaskScheduling(sal_uInt64& nMinPeriod, sal_uInt64 nTime);
    void  ImplInvoke(sal_uInt64 nTime);
    static Scheduler* ImplGetHighestPrioTask( bool bTimer );
    bool ImplIsScheduleReady(sal_uInt32 nUpdateStack);
    void ImplDispose();
};

typedef ::std::list< ImplSchedulerData* > ImplScheduler_t;

#endif // INCLUDED_VCL_SCHEDULER_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/timer.hxx b/include/vcl/timer.hxx
index 8835291..f6f3dea 100644
--- a/include/vcl/timer.hxx
+++ b/include/vcl/timer.hxx
@@ -23,6 +23,8 @@
#include <tools/link.hxx>
#include <vcl/scheduler.hxx>

struct ImplSVData;

class VCL_DLLPUBLIC Timer : public Scheduler
{
protected:
diff --git a/sfx2/source/appl/appinit.cxx b/sfx2/source/appl/appinit.cxx
index d6315d2..1d577c7 100644
--- a/sfx2/source/appl/appinit.cxx
+++ b/sfx2/source/appl/appinit.cxx
@@ -109,7 +109,7 @@

    // Timers may access the SfxApplication and are only deleted in
    // Application::Quit(), which is asynchronous (PostUserEvent) - disable!
    Scheduler::ImplDeInitScheduler();
    Scheduler::ImplDeInitScheduler(false); // false only delete the list

    SfxApplication* pApp = SfxGetpApp();
    pApp->Broadcast( SfxSimpleHint( SFX_HINT_DEINITIALIZING ) );
diff --git a/vcl/inc/svdata.hxx b/vcl/inc/svdata.hxx
index 595812a..1261b28 100644
--- a/vcl/inc/svdata.hxx
+++ b/vcl/inc/svdata.hxx
@@ -313,7 +313,7 @@
    bool                    mbDeInit;                       // Is VCL deinitializing
    sal_uLong               mnThreadCount;                  // is VCL MultiThread enabled
    ImplConfigData*         mpFirstConfigData;              // pointer to the first config block
    ImplSchedulerData*      mpFirstSchedulerData;           // list of all running tasks
    ImplScheduler_t*        maSchedulers;                    // list of all running tasks
    SalTimer*               mpSalTimer;                     // interface to sal event loop/timers
    SalI18NImeStatus*       mpImeStatus;                    // interface to ime status window
    SalSystem*              mpSalSystem;                    // SalSystem interface
diff --git a/vcl/source/app/idle.cxx b/vcl/source/app/idle.cxx
index 0dd8593..e2fa915 100644
--- a/vcl/source/app/idle.cxx
+++ b/vcl/source/app/idle.cxx
@@ -62,5 +62,4 @@
    return 1;
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx
index c3cea78..a440045 100644
--- a/vcl/source/app/scheduler.cxx
+++ b/vcl/source/app/scheduler.cxx
@@ -21,85 +21,88 @@
#include <tools/time.hxx>
#include <vcl/scheduler.hxx>
#include <vcl/timer.hxx>
#include <algorithm>
#include <saltimer.hxx>

#define MAX_TIMER_PERIOD    SAL_MAX_UINT64

void ImplSchedulerData::Invoke()
void Scheduler::ImplInvoke(sal_uInt64 nTime)
{
    if (mbDelete || mbInScheduler )
    mnUpdateTime = nTime;

    if (mpSchedulerData->mbDelete || mbInScheduler )
        return;

    // prepare Scheduler Object for deletion after handling
    mpScheduler->SetDeletionFlags();
    SetDeletionFlags();

    // invoke it
    mbInScheduler = true;
    mpScheduler->Invoke();
    Invoke();
    mbInScheduler = false;
}

ImplSchedulerData *ImplSchedulerData::GetMostImportantTask( bool bTimer )
Scheduler* Scheduler::ImplGetHighestPrioTask( bool bTimer )
{
    ImplSVData*     pSVData = ImplGetSVData();
    ImplSchedulerData *pMostUrgent = NULL;
    ImplSVData*     pSVData     = ImplGetSVData();
    Scheduler *     pMostUrgent = NULL;

    for ( ImplSchedulerData *pSchedulerData = pSVData->mpFirstSchedulerData; pSchedulerData; pSchedulerData = pSchedulerData->mpNext )
    std::for_each(pSVData->maSchedulers->begin(), pSVData->maSchedulers->end(),
        [&pSVData, bTimer, &pMostUrgent] (ImplSchedulerData *rScheduler)
    {
        if ( !pSchedulerData->mpScheduler || pSchedulerData->mbDelete || pSchedulerData->mnUpdateStack >= pSVData->mnUpdateStack
            || !pSchedulerData->mpScheduler->ReadyForSchedule( bTimer ) || !pSchedulerData->mpScheduler->IsActive())
            continue;
        if (!pMostUrgent)
            pMostUrgent = pSchedulerData;
        else
        if ( rScheduler->mpScheduler &&
            rScheduler->mpScheduler->ImplIsScheduleReady(pSVData->mnUpdateStack) &&
            rScheduler->mpScheduler->ReadyForSchedule( bTimer ) &&
            rScheduler->mpScheduler->IsActive() )
        {
            // Find the highest priority.
            // If the priority of the current task is higher (numerical value is lower) than
            // the priority of the most urgent, the current task gets the new most urgent.
            if ( pSchedulerData->mpScheduler->GetPriority() < pMostUrgent->mpScheduler->GetPriority() )
                pMostUrgent = pSchedulerData;
            if (!pMostUrgent)
                pMostUrgent = rScheduler->mpScheduler;
            else
            {
                // Find the highest priority.
                // If the priority of the current task is higher (numerical value is lower) than
                // the priority of the most urgent, the current task gets the new most urgent.
                if ( rScheduler->mpScheduler->GetPriority() < pMostUrgent->GetPriority() )
                    pMostUrgent = rScheduler->mpScheduler;
            }
        }
    }
    });

    return pMostUrgent;
}

void Scheduler::SetDeletionFlags()
{
    mpSchedulerData->mbDelete = true;
    mbActive = false;
    Stop();
}

void Scheduler::ImplDeInitScheduler()
void Scheduler::ImplDeInitScheduler(bool bAll /*=true*/)
{
    ImplSVData*     pSVData = ImplGetSVData();
    ImplSchedulerData*  pSchedulerData = pSVData->mpFirstSchedulerData;
    ImplSVData* pSVData = ImplGetSVData();

    if (pSVData->mpSalTimer)
    {
        pSVData->mpSalTimer->Stop();
    }

    if ( pSchedulerData )
    pSVData->maSchedulers->remove_if( [] (ImplSchedulerData *rSchedulerData)
    {
        do
        if(rSchedulerData->mpScheduler != NULL)
        {
            ImplSchedulerData* pTempSchedulerData = pSchedulerData;
            if ( pSchedulerData->mpScheduler )
            {
                pSchedulerData->mpScheduler->mbActive = false;
                pSchedulerData->mpScheduler->mpSchedulerData = NULL;
            }
            pSchedulerData = pSchedulerData->mpNext;
            delete pTempSchedulerData;
            rSchedulerData->mpScheduler->ImplDispose();
        }
        while ( pSchedulerData );
        else
            delete rSchedulerData;

        pSVData->mpFirstSchedulerData   = NULL;
        pSVData->mnTimerPeriod      = 0;
        return true;
    });

    if(bAll)
    {
        delete pSVData->maSchedulers;
        pSVData->maSchedulers = NULL;
    }

    delete pSVData->mpSalTimer;
    pSVData->mpSalTimer = 0;
    pSVData->mpSalTimer  = NULL;
}

void Scheduler::CallbackTaskScheduling(bool ignore)
@@ -113,52 +116,30 @@
{
    // process all pending Tasks
    // if bTimer True, only handle timer
    ImplSchedulerData* pSchedulerData = NULL;
    ImplSchedulerData* pPrevSchedulerData = NULL;
    ImplSVData*        pSVData = ImplGetSVData();
    sal_uInt64         nTime = tools::Time::GetSystemTicks();
    sal_uInt64         nMinPeriod = MAX_TIMER_PERIOD;
    Scheduler*  pScheduler = NULL;
    ImplSVData* pSVData = ImplGetSVData();
    sal_uInt64  nTime = tools::Time::GetSystemTicks();
    sal_uInt64  nMinPeriod = MAX_TIMER_PERIOD;

    pSVData->mnUpdateStack++;

    // tdf#91727 - NB. bTimer is ultimately not used
    if ((pSchedulerData = ImplSchedulerData::GetMostImportantTask(bTimer)))
    {
        pSchedulerData->mnUpdateTime = nTime;
        pSchedulerData->Invoke();
    }
    if ((pScheduler = Scheduler::ImplGetHighestPrioTask(bTimer)) != NULL)
        pScheduler->ImplInvoke(nTime);

    pSchedulerData = pSVData->mpFirstSchedulerData;
    while ( pSchedulerData )
    pSVData->maSchedulers->remove_if( [&nMinPeriod, nTime, pSVData] (ImplSchedulerData *rSchedulerData)
    {
        if( pSchedulerData->mbInScheduler )
        {
            pPrevSchedulerData = pSchedulerData;
            pSchedulerData = pSchedulerData->mpNext;
        }
        // Should Task be released from scheduling?
        else if ( pSchedulerData->mbDelete )
        {
            if ( pPrevSchedulerData )
                pPrevSchedulerData->mpNext = pSchedulerData->mpNext;
            else
                pSVData->mpFirstSchedulerData = pSchedulerData->mpNext;
            if ( pSchedulerData->mpScheduler )
                pSchedulerData->mpScheduler->mpSchedulerData = NULL;
            ImplSchedulerData* pTempSchedulerData = pSchedulerData;
            pSchedulerData = pSchedulerData->mpNext;
            delete pTempSchedulerData;
        }
        if (rSchedulerData->mpScheduler)
            return rSchedulerData->mpScheduler->ImplHandleTaskScheduling(nMinPeriod, nTime);
        else
        {
            pSchedulerData->mnUpdateStack = 0;
            nMinPeriod = pSchedulerData->mpScheduler->UpdateMinPeriod( nMinPeriod, nTime );
            pPrevSchedulerData = pSchedulerData;
            pSchedulerData = pSchedulerData->mpNext;
            delete rSchedulerData;
            return true;
        }
    }
    });

    // delete clock if no more timers available
    if ( !pSVData->mpFirstSchedulerData )
    if ( pSVData->maSchedulers->empty() )
    {
        if ( pSVData->mpSalTimer )
            pSVData->mpSalTimer->Stop();
@@ -168,6 +149,7 @@
    {
        Timer::ImplStartTimer( pSVData, nMinPeriod );
    }

    pSVData->mnUpdateStack--;
}

@@ -178,34 +160,22 @@

void Scheduler::Start()
{
    ImplSVData* pSVData = ImplGetSVData();
    // Mark timer active
    mbActive = true;

    ImplSVData* pSVData = ImplGetSVData();
    if ( !mpSchedulerData )
    {
        mpSchedulerData = new ImplSchedulerData;
        mpSchedulerData->mpScheduler = this;
        // insert Scheduler
        mpSchedulerData                = new ImplSchedulerData;
        mpSchedulerData->mpScheduler   = this;
        mpSchedulerData->mbInScheduler = false;

        // insert last due to SFX!
        ImplSchedulerData* pPrev = NULL;
        ImplSchedulerData* pData = pSVData->mpFirstSchedulerData;
        while ( pData )
        {
            pPrev = pData;
            pData = pData->mpNext;
        }
        mpSchedulerData->mpNext = NULL;
        if ( pPrev )
            pPrev->mpNext = mpSchedulerData;
        else
            pSVData->mpFirstSchedulerData = mpSchedulerData;
        mbInScheduler   = false;
        pSVData->maSchedulers->push_back(mpSchedulerData);
    }
    mpSchedulerData->mbDelete      = false;
    mpSchedulerData->mnUpdateTime  = tools::Time::GetSystemTicks();
    mpSchedulerData->mnUpdateStack = pSVData->mnUpdateStack;

    mpSchedulerData->mbDelete = false;
    mnUpdateTime              = tools::Time::GetSystemTicks();
    mnUpdateStack             = pSVData->mnUpdateStack;
}

void Scheduler::Stop()
@@ -221,7 +191,7 @@
    if ( IsActive() )
        Stop();

    mbActive          = false;
    mbActive   = false;
    mePriority = rScheduler.mePriority;

    if ( rScheduler.IsActive() )
@@ -233,16 +203,18 @@
Scheduler::Scheduler(const sal_Char *pDebugName):
    mpSchedulerData(NULL),
    mpDebugName(pDebugName),
    mePriority(SchedulerPriority::HIGH),
    mbActive(false)
    mePriority(SchedulerPriority::HIGH),

    mbActive(false),

    mnUpdateTime(0)
{
}

Scheduler::Scheduler( const Scheduler& rScheduler ):
    mpSchedulerData(NULL),
    mpDebugName(rScheduler.mpDebugName),
    mePriority(rScheduler.mePriority),
    mbActive(false)
    mePriority(SchedulerPriority::HIGH),

    mbActive(false),

    mnUpdateTime(0)
{
    if ( rScheduler.IsActive() )
        Start();
@@ -257,3 +229,44 @@
    }
}

bool Scheduler::ImplIsScheduleReady(sal_uInt32 nUpdateStack)
{
    return !mpSchedulerData->mbDelete && (mnUpdateStack <= nUpdateStack);
}

void Scheduler::ImplDispose()
{
    mpSchedulerData->mpScheduler = NULL;
    delete mpSchedulerData;
    mpSchedulerData = NULL;
}

void Scheduler::ImplInitScheduler()
{
    ImplSVData* pSVData = ImplGetSVData();

    if(pSVData->maSchedulers == NULL)
        pSVData->maSchedulers = new ImplScheduler_t;
}


bool  Scheduler::ImplHandleTaskScheduling(sal_uInt64 &nMinPeriod, sal_uInt64 nTime)
{
    // process all pending Tasks
    if( !mbInScheduler )
    {
        // Should Task be released from scheduling?
        if ( !mpSchedulerData->mbDelete )
        {
            mnUpdateStack = 0;
            nMinPeriod    = UpdateMinPeriod( nMinPeriod, nTime );
        }
        else
        {
            ImplDispose();
            return true;
        }
    }

    return false;
}
diff --git a/vcl/source/app/svdata.cxx b/vcl/source/app/svdata.cxx
index 8b1a2a6..a7c184a 100644
--- a/vcl/source/app/svdata.cxx
+++ b/vcl/source/app/svdata.cxx
@@ -81,7 +81,8 @@
ImplSVData::ImplSVData()
{
    // init global instance data
    memset( this, 0, sizeof( ImplSVData ) );
    memset( this, 0, sizeof( ImplSVData ));

    maHelpData.mbAutoHelpId = true;
    maNWFData.maMenuBarHighlightTextColor = Color( COL_TRANSPARENT );
}
diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx
index 3f9026e..dda1e29 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -251,6 +251,8 @@
    // remember Main-Thread-Id
    pSVData->mnMainThreadId = ::osl::Thread::getCurrentIdentifier();

    Scheduler::ImplInitScheduler();

    // Initialize Sal
    pSVData->mpDefInst = CreateSalInstance();
    if ( !pSVData->mpDefInst )
@@ -371,6 +373,7 @@

    if ( pSVData->maAppData.mpIdleMgr )
        delete pSVData->maAppData.mpIdleMgr;

    Scheduler::ImplDeInitScheduler();

    if ( pSVData->maWinData.mpMsgBoxImgList )
diff --git a/vcl/source/app/timer.cxx b/vcl/source/app/timer.cxx
index 7d92283..b6c2aba 100644
--- a/vcl/source/app/timer.cxx
+++ b/vcl/source/app/timer.cxx
@@ -23,7 +23,6 @@
#include <svdata.hxx>
#include <salinst.hxx>

#define MAX_TIMER_PERIOD   SAL_MAX_UINT64

void Timer::ImplStartTimer( ImplSVData* pSVData, sal_uInt64 nMS )
{
@@ -45,23 +44,23 @@
        // if no AutoTimer than stop
        if ( !mbAuto )
        {
            mpSchedulerData->mbDelete = true;
            mbActive = false;
            Scheduler::SetDeletionFlags();
        }
}

bool Timer::ReadyForSchedule( bool bTimer )
{
    (void)bTimer;
    return (mpSchedulerData->mnUpdateTime + mnTimeout) <= tools::Time::GetSystemTicks();
    return (mnUpdateTime + mnTimeout) <= tools::Time::GetSystemTicks();
}

sal_uInt64 Timer::UpdateMinPeriod( sal_uInt64 nMinPeriod, sal_uInt64 nTime )
{
    sal_uInt64 nNewTime = tools::Time::GetSystemTicks();
    sal_uInt64 nDeltaTime;

    //determine smallest time slot
    if( mpSchedulerData->mnUpdateTime == nTime )
    if( mnUpdateTime == nTime )
    {
       nDeltaTime = mnTimeout;
       if( nDeltaTime < nMinPeriod )
@@ -69,7 +68,7 @@
    }
    else
    {
        nDeltaTime = mpSchedulerData->mnUpdateTime + mnTimeout;
        nDeltaTime = mnUpdateTime + mnTimeout;
        if( nDeltaTime < nNewTime )
            nMinPeriod = 1;
        else